package com.hup.utils.commons.hardware;

import com.hup.utils.commons.interfaces.Callers.Caller1;
import com.hup.utils.commons.start.AppIcon;
import com.hup.utils.commons.start.LookThemeUtil;

import javax.swing.JScrollPane;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;

/**
 * @author hugan
 * @date 2020/1/4
 */
public class MouseUtil {
    /**
     * 鼠标 后退键
     */
    public static final int BUTTON_BACKWARD = 4;
    /**
     * 鼠标 前进键
     */
    public static final int BUTTON_FORWARD = 5;

    /**
     * 全局获取鼠标位置
     *
     * @return 坐标范围[正负无穷大]; 场景: 多屏幕时,鼠标位置可能是负数
     */
    public static Point getLocation() {
        PointerInfo pointerInfo = MouseInfo.getPointerInfo();
        if (pointerInfo == null) {
            //锁屏界面输入密码时,会null
            return new Point(-1000000, -1000000);
        }
        return pointerInfo.getLocation();
    }

    public static Point getLocationNullable() {
        PointerInfo pointerInfo = MouseInfo.getPointerInfo();
        if (pointerInfo == null) return null;
        return pointerInfo.getLocation();
    }

    /**
     * @return 本次事件按下的是[左键]
     * @apiNote 不适用于拖拽事件!! 如果是拖拽事件,则要用 SwingUtilities.isLeftMouseButton(e)才能获取/判断已按下的键位
     */
    public static boolean isLeftButton(MouseEvent e) {
        /*
        SwingUtilities.isLeftMouseButton(e)
        不能使用!!!看源码
        它是功能是:只要[按键x]有被按下,就返回true;不符合本方法的目的
         */
        return e.getButton() == MouseEvent.BUTTON1;
    }

    public static boolean isMiddleButton(MouseEvent e) {
        return e.getButton() == MouseEvent.BUTTON2;
    }

    public static boolean isRightButton(MouseEvent e) {
        return e.getButton() == MouseEvent.BUTTON3;
    }

    /**
     * @apiNote 某些鼠标是倒过来的?
     */
    public static boolean isForwardButton(MouseEvent e) {
        return e.getButton() == BUTTON_FORWARD;
    }

    /**
     * @apiNote 某些鼠标是倒过来的?
     */
    public static boolean isBackwardButton(MouseEvent e) {
        return e.getButton() == BUTTON_BACKWARD;
    }

    /**
     * @return 滚轮事件的方向
     * @apiNote 某些奇葩鼠标是反过来的!?
     */
    public static boolean isUp(MouseWheelEvent e) {
        int rotation = e.getWheelRotation();
        return rotation < 0;
    }

    /**
     * @return true-前进按键;false-后退按键;null-都不是
     * @apiNote 某些鼠标是倒过来的?
     */
    public static Boolean getDirection(MouseEvent e) {
        if (e.getButton() == BUTTON_BACKWARD) {
            return false;
        } else if (e.getButton() == BUTTON_FORWARD) {
            return true;
        }
        return null;
    }

    /**
     * @apiNote 组件在 {@link JScrollPane}中时,未遇到x,y是负数的情况
     */
    public static boolean isInsideComponent(Component c, MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        Rectangle bounds = c.getBounds();
        if (x < 0 || x > bounds.width - 1) return false;
        if (y < 0 || y > bounds.height - 1) return false;
        return true;
    }

    /**
     * 使用自定义图标创建光标
     *
     * @param imgPath jar内的图像路径
     * @param desc 光标描述,唯一的
     * @param hotSpotCaller 光标触点在图案的坐标[x,y起始于0],[Dimension]:图标的最大尺寸;[]
     * </pre>
     */
    public static Cursor getCursor(String imgPath, String desc, Caller1<Point, Dimension> hotSpotCaller) {
        Image image = AppIcon.getIcon(imgPath, desc).getImage();
        Dimension max = Toolkit.getDefaultToolkit().getBestCursorSize(image.getWidth(null), image.getHeight(null));
        return Toolkit.getDefaultToolkit().createCustomCursor(image, hotSpotCaller.get(max), desc);
    }

    /**
     * {@link #updateResizeCursor(int, int, Component, Rectangle, int)}
     */
    public static int updateResizeCursor(int mX, int mY, Component targetComponent, Rectangle bounds) {
        return updateResizeCursor(mX, mY, targetComponent, bounds, (int) (5 * LookThemeUtil.getScreenScale()));
    }

    /**
     * 将光标设成调节尺寸的'光标图案'
     *
     * @param mX 当前鼠标位置
     * @param targetComponent [被调节尺寸的组件],或[需要更新光标的组件]
     * @param bounds [被调节尺寸的组件]的尺寸,或[当前尺寸]
     * @param edgeWidth 边缘的宽度
     * @return 结果光标, 可以用来判断当前所在的[域]
     * @apiNote 在鼠标在 bounds边缘上时,就不是[默认光标]了,可以调节大小
     */
    public static int updateResizeCursor(int mX, int mY, Component targetComponent, Rectangle bounds, int edgeWidth) {
        //边界坐标
        int xMax = bounds.x + bounds.width - edgeWidth;
        int yMax = bounds.y + bounds.height - edgeWidth;
        /*
            N
           W  E
            S
         */
        int type;
        if (mY <= bounds.y + edgeWidth) {
            if (mX <= bounds.x + edgeWidth) {
                type = Cursor.NW_RESIZE_CURSOR;
            } else if (mX >= xMax) {
                type = Cursor.NE_RESIZE_CURSOR;
            } else {
                type = Cursor.N_RESIZE_CURSOR;
            }
        } else if (mY >= yMax) {
            if (mX <= bounds.x + edgeWidth) {
                type = Cursor.SW_RESIZE_CURSOR;
            } else if (mX >= xMax) {
                type = Cursor.SE_RESIZE_CURSOR;
            } else {
                type = Cursor.S_RESIZE_CURSOR;
            }
        } else {
            if (mX <= bounds.x + edgeWidth) {
                type = Cursor.W_RESIZE_CURSOR;
            } else if (mX >= xMax) {
                type = Cursor.E_RESIZE_CURSOR;
            } else {
                type = Cursor.DEFAULT_CURSOR;
            }
        }
        targetComponent.setCursor(Cursor.getPredefinedCursor(type));
        return type;
    }

}
